// server_core.cpp
#include "server_core.h"
#include "user_manager.h"
#include "room_manager.h"
#include "utils.h"

#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <algorithm>
#include <sstream>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <cstring>
#include <cstdlib>
#include <signal.h>
#include <fstream>

using namespace std;

vector<int> client_fds;
RoomManager room_manager;
bool server_running = true;
int global_server_fd = -1;

void handle_sigint(int signum) {
    cout << "\n[Server] Caught SIGINT, shutting down..." << endl;
    if (global_server_fd != -1) close(global_server_fd);
    cout << "\033[0m" << flush;
    log_to_file("[Server] Shutdown by SIGINT.\n");
    exit(0);
}

void broadcast(int sender_fd, const string& msg, bool include_room_prefix = true) {
    string room = room_manager.get_user_room(sender_fd);
    string prefix = include_room_prefix ? ("[" + room + "] ") : "";
    for (int fd : client_fds) {
        if (fd != sender_fd && room_manager.get_user_room(fd) == room) {
            string full_msg = prefix + msg;
            send(fd, full_msg.c_str(), full_msg.size(), 0);
        }
    }
}

void handle_message(int fd, const string& msg, fd_set& all_set, size_t& i) {
    if (msg == "/quit\n") {
        string left = color_wrap(get_current_time() + " [Server] " + get_nickname(fd) + " quit.\n", Color::YELLOW);
        broadcast(fd, left);
        send(fd, left.c_str(), left.size(), 0);
        cout << left; log_to_file(left);
        FD_CLR(fd, &all_set); close(fd);
        remove_user(fd); room_manager.leave(fd);
        client_fds.erase(client_fds.begin() + i); --i;
        return;
    }
    else if (msg.rfind("/rename", 0) == 0) {
        istringstream iss(msg); string cmd, newname; iss >> cmd >> newname;
        if (newname.empty() || is_nickname_taken(newname)) {
            string err = color_wrap("[Server] Invalid or taken name.\n", Color::RED);
            send(fd, err.c_str(), err.size(), 0); return;
        }
        string old = get_nickname(fd);
        set_nickname(fd, newname);
        string notice = color_wrap(get_current_time() + " [Server] " + old + " → " + newname + "\n", Color::YELLOW);
        broadcast(fd, notice);
        send(fd, notice.c_str(), notice.size(), 0);
        log_to_file("[Rename] " + old + " → " + newname + "\n"); return;
    }
    else if (msg == "/room\n") {
        string output = "[Room List]\n";
        for (const auto& [name, members] : room_manager.get_all_rooms()) {
            output += "- " + name + " (" + to_string(members.size()) + ")\n";
        }
        output += "\n[Server] You are in room: " + room_manager.get_user_room(fd) + "\n";
        send(fd, color_wrap(output, Color::CYAN).c_str(), output.size(), 0); return;
    }
    else if (msg == "/leave\n") {
        string old_room = room_manager.get_user_room(fd);
        room_manager.leave(fd);
        string err;
        room_manager.join(fd, "lobby", "", err);
        string notice = color_wrap(get_current_time() + " [Server] " + get_nickname(fd) + " left room: " + old_room + "\n", Color::YELLOW);
        broadcast(fd, notice);
        send(fd, color_wrap("[Server] You have returned to lobby.\n", Color::CYAN).c_str(), 44, 0);
        log_to_file("[Leave] " + get_nickname(fd) + " → lobby\n"); return;
    }
    else if (msg == "/members\n") {
        string room = room_manager.get_user_room(fd);
        auto fds = room_manager.get_members(room);
        string list = color_wrap("[Room members: " + room + "]\n", Color::CYAN);
        for (int x : fds) list += "- " + get_nickname(x) + "\n";
        send(fd, list.c_str(), list.size(), 0); return;
    }
    else if (msg == "/help\n") {
        string help =
            color_wrap("[Command Help]\n", Color::CYAN) +
            "/who            - Show all online users\n"
            "/room           - Show your current room\n"
            "/members        - List members in your room\n"
            "/join <room> [pwd] - Join or create a room\n"
            "/leave          - Leave current room\n"
            "/rename <name>  - Change your nickname\n"
            "/to <name> msg  - Private message\n"
            "/quit           - Disconnect\n"
            "/help           - Show this help menu\n\n";
        send(fd, help.c_str(), help.size(), 0); return;
    }
    else if (msg.rfind("/who", 0) == 0) {
        string list = color_wrap("[Online users]\n", Color::CYAN);
        for (auto& name : get_all_nicknames()) list += "- " + name + "\n";
        send(fd, list.c_str(), list.size(), 0); return;
    }
    else if (msg.rfind("/join", 0) == 0) {
        istringstream iss(msg); string cmd, room_name, pwd; iss >> cmd >> room_name >> pwd;
        if (room_name.empty()) {
            string err = color_wrap("[Server] Usage: /join <room> [pwd]\n", Color::RED);
            send(fd, err.c_str(), err.size(), 0); return;
        }
        string join_err;
        if (!room_manager.join(fd, room_name, pwd, join_err)) {
            string err = color_wrap("[Server] " + join_err + "\n", Color::RED);
            send(fd, err.c_str(), err.size(), 0); return;
        }
        string join_msg = color_wrap(get_current_time() + " [Server] " + get_nickname(fd) + " joined room: " + room_name + "\n", Color::GREEN);
        send(fd, join_msg.c_str(), join_msg.size(), 0);
        broadcast(fd, join_msg); log_to_file("[Join] " + get_nickname(fd) + " → " + room_name + "\n"); return;
    }
    else if (msg.rfind("/to", 0) == 0) {
        string content = msg.substr(4);
        size_t space = content.find(' ');
        if (space == string::npos) {
            string err = color_wrap("[Server] Usage: /to <name> <msg>\n", Color::RED);
            send(fd, err.c_str(), err.size(), 0); return;
        }
        string target = content.substr(0, space);
        string real_msg = content.substr(space + 1);
        int target_fd = get_fd_by_name(target);
        if (target_fd == -1) {
            string err = color_wrap("[Server] No such user: " + target + "\n", Color::RED);
            send(fd, err.c_str(), err.size(), 0); return;
        }
        string from = get_nickname(fd);
        string final_msg = color_wrap(get_current_time() + " [Private from " + from + "] " + real_msg, Color::CYAN);
        send(target_fd, final_msg.c_str(), final_msg.size(), 0);
        send(fd, final_msg.c_str(), final_msg.size(), 0);
        log_to_file("[Private] " + from + " → " + target + ": " + real_msg + "\n"); return;
    }
    else {
        string full_msg = color_wrap(get_current_time() + " [" + get_nickname(fd) + "] " + msg, Color::RESET);
        broadcast(fd, full_msg);
        cout << full_msg;
        log_to_file(full_msg);
    }
}
void run_server(int port) {
    ofstream clear("chat_log.txt", ios::out); 
    clear.close();

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) { perror("socket failed"); exit(1); }

    signal(SIGINT, handle_sigint);
    global_server_fd = server_fd;

    sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(server_fd, (const sockaddr*)&addr, sizeof(addr)) < 0) { perror("bind failed"); exit(1); }
    if (listen(server_fd, 10) < 0) { perror("listen"); exit(1); }

    fd_set all_set, read_set;
    FD_ZERO(&all_set);
    FD_SET(server_fd, &all_set);
    int max_fd = server_fd;

    cout << "Server listening on port " << port << "..." << endl;

    while (server_running) {
        read_set = all_set;
        if (select(max_fd + 1, &read_set, nullptr, nullptr, nullptr) < 0) break;

        // 处理新连接
        if (FD_ISSET(server_fd, &read_set)) {
            sockaddr_in cli_addr{};
            socklen_t len = sizeof(cli_addr);
            int client_fd = accept(server_fd, (sockaddr*)&cli_addr, &len);
            if (client_fd < 0) continue;

            struct timeval timeout {10, 0};
            setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));

            char name_buf[32];
            int n = recv(client_fd, name_buf, sizeof(name_buf) - 1, 0);
            if (n <= 0) { close(client_fd); continue; }

            name_buf[n] = '\0';
            string nickname = name_buf;

            if (is_nickname_taken(nickname)) {
                string msg = color_wrap("[Server] Nickname already in use.\n", Color::RED);
                send(client_fd, msg.c_str(), msg.size(), 0);
                close(client_fd);
                continue;
            }

            add_user(client_fd, nickname);
            client_fds.push_back(client_fd);
            FD_SET(client_fd, &all_set);
            max_fd = max(max_fd, client_fd);

            string err;
            room_manager.join(client_fd, "lobby", "", err);

            string msg = color_wrap(get_current_time() + " [Server] " + nickname + " joined.\n", Color::GREEN);
            broadcast(client_fd, msg);
            send(client_fd, msg.c_str(), msg.size(), 0);
            cout << msg;
            log_to_file(msg);
        }

        // 处理现有客户端消息
        for (size_t i = 0; i < client_fds.size(); ++i) {
            int fd = client_fds[i];
            if (!FD_ISSET(fd, &read_set)) continue;

            char buf[1024];
            int n = recv(fd, buf, sizeof(buf) - 1, 0);
            if (n <= 0) {
                string left = color_wrap(get_current_time() + " [Server] " + get_nickname(fd) + " left.\n", Color::YELLOW);
                broadcast(fd, left);
                cout << left;
                log_to_file(left);
                FD_CLR(fd, &all_set);
                close(fd);
                remove_user(fd);
                room_manager.leave(fd);
                client_fds.erase(client_fds.begin() + i);
                --i;
                continue;
            }

            buf[n] = '\0';
            string msg(buf);
            handle_message(fd, msg, all_set, i);
        }
    }

    close(server_fd);
    cout << "\033[0m" << flush;
}

